Skip to main content

Log Format

The access log records every HTTP request handled by NGINX.

It is essential for:

  • Security monitoring (attacks, scans, abuse)
  • Performance analysis (latency, bottlenecks)
  • Debugging application issues
  • Traffic analytics
  • Compliance and auditing

Each log entry represents one completed request.

How Access Logging Works Internally​

  1. Client sends request
  2. NGINX processes request
  3. Response is sent
  4. After response, NGINX writes a log line using:
    • A log format
    • A log destination (file/syslog/stdout)

Core Directives for Access Logs​

log_format (Define Log Structure)​

log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';

Defines what data is logged and how it is structured

access_log (Enable Logging)​

access_log /var/log/nginx/access.log main;

Uses:

  • Log file path
  • Log format name

Default NGINX Access Log Format (Combined)​

NGINX’s default format is similar to Apache’s combined format:

log_format combined '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';

Example Log Entry

203.0.113.10 - - [22/Jan/2026:10:15:43 +0000]
"GET /login HTTP/1.1" 200 532
"https://example.com" "Mozilla/5.0"

Explanation of Common Access Log Variables​

VariableDescriptionSecurity / Monitoring Use
$remote_addrClient IP addressIdentify attackers
$remote_userAuthenticated userAudit
$time_localRequest timeIncident timeline
$requestFull request lineDetect exploits
$statusHTTP response codeError monitoring
$body_bytes_sentResponse sizeTraffic analysis
$http_refererReferrer URLCSRF detection
$http_user_agentClient softwareBot detection
log_format security '$remote_addr '
'[$time_iso8601] '
'"$request" '
'$status '
'$request_time '
'"$http_user_agent"';

Example Entry

198.51.100.25 [2026-01-22T10:17:11+00:00]
"POST /login HTTP/1.1" 401 0.532 "curl/7.88.1"

Performance & Latency Monitoring Format​

log_format performance '$remote_addr '
'"$request" '
'$status '
'rt=$request_time '
'uct=$upstream_connect_time '
'uht=$upstream_header_time '
'urt=$upstream_response_time';

Example

203.0.113.5 "GET /api HTTP/1.1" 200 rt=0.120 uct=0.003 uht=0.020 urt=0.115
FieldMeaning
rtTotal request time
uctUpstream connect time
uhtTime to receive headers
urtBackend response time

JSON Access Log Format (Best for Modern Monitoring)​

log_format json escape=json
'{'
'"time":"$time_iso8601",'
'"remote_addr":"$remote_addr",'
'"method":"$request_method",'
'"uri":"$request_uri",'
'"status":$status,'
'"request_time":$request_time,'
'"user_agent":"$http_user_agent"'
'}';

Example JSON Log

{
"time":"2026-01-22T10:19:12+00:00",
"remote_addr":"192.0.2.15",
"method":"GET",
"uri":"/api/users",
"status":200,
"request_time":0.083,
"user_agent":"Mozilla/5.0"
}

Conditional Access Logging (Reduce Noise)​

Log Only Errors (Status ≥ 400)

map $status $log_errors {
~^[45] 1;
default 0;
}

access_log /var/log/nginx/error_access.log security if=$log_errors;

Disable Access Logging (Static Content)​

location /assets/ {
access_log off;
expires 30d;
}

Logging Real Client IP (Behind Proxy)​

set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;

Then log:

log_format realip '$remote_addr "$request" $status';

Common Logging Mistakes​

MistakeImpact
Logging without request timeHard to debug slowness
Not using JSON logsPainful parsing
Missing real IPWrong attacker identity
Logging everythingDisk exhaustion
No log rotationServer crash

Log Rotation (Essential)​

NGINX does not rotate logs itself.

Use logrotate:

/var/log/nginx/*.log {
daily
rotate 14
compress
missingok
notifempty
create 0640 nginx adm
postrotate
systemctl reload nginx
endscript
}
log_format secure_json escape=json
'{'
'"time":"$time_iso8601",'
'"ip":"$remote_addr",'
'"request":"$request",'
'"status":$status,'
'"rt":$request_time,'
'"ua":"$http_user_agent"'
'}';

access_log /var/log/nginx/access.log secure_json;